﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Xml.Linq;
using System.IO;
using System.IO.Compression;
using System.Runtime.Serialization.Json;

namespace Oryon.Extensions.CTS
{
    /// <summary>
    /// Extension methods for Common Types something like that string,int etc.
    /// Also contains methods for unprimitive types something like that IEnumerable
    /// </summary>
    public static class Functions
    {
        /// <summary>
        /// to blend some collection in random order
        /// </summary>
        /// <typeparam name="T">source type for collection</typeparam>
        /// <param name="source">source collection</param>
        /// <returns>blended collection</returns>
        public static IEnumerable<T> BlendRandom<T>(this IEnumerable<T> source)
        {
            return source.OrderBy(t => Guid.NewGuid());
        }

        /// <summary>
        /// To hash any string value
        /// </summary>
        /// <param name="source">Source string something like that password</param>
        /// <param name="hashType">Hashtype. Available types are MD5, SHA1, SHA256, SHA384 and SHA512</param>
        /// <returns></returns>
        public static string HashedValue(this string source, HashType hashType = HashType.MD5)
        {
            string hashedResult = string.Empty;
            byte[] bufferedArray = Encoding.UTF8.GetBytes(source);

            switch (hashType)
            {
                case HashType.MD5:
                    hashedResult=ConvertHashedToString(MD5.Create().ComputeHash(bufferedArray));
                    break;
                case HashType.SHA1:
                    hashedResult = ConvertHashedToString(SHA1.Create().ComputeHash(bufferedArray));
                    break;
                case HashType.SHA256:
                    hashedResult = ConvertHashedToString(SHA256.Create().ComputeHash(bufferedArray));
                    break;
                case HashType.SHA384:
                    hashedResult = ConvertHashedToString(SHA384.Create().ComputeHash(bufferedArray));
                    break;
                case HashType.SHA512:
                    hashedResult = ConvertHashedToString(SHA512.Create().ComputeHash(bufferedArray));
                    break;
                default:
                    hashedResult = ConvertHashedToString(MD5.Create().ComputeHash(bufferedArray));
                    break;
            }

            return hashedResult;
        }

        private static string ConvertHashedToString(byte[] value)
        {
            string result = string.Empty;
            for (int i = 0; i < value.Length; i++)
            {
                result += (char)value[i];
            }
            return result;
        }

        /// <summary>
        /// Write any .Net Collection' s content to XElement
        /// </summary>
        /// <typeparam name="T">source type</typeparam>
        /// <param name="source">source collection</param>
        /// <param name="rootName">Sample root name</param>
        /// <returns>If succedded it returns a XElement that contains list instances</returns>
        public static XElement ToXml<T>(this IEnumerable<T> source,string rootName="content")
        {
            XElement root = new XElement(rootName);
            Type type = typeof(T);
            var properties = type.GetProperties();

            foreach (var item in source)
            {
                XElement itemElement = new XElement(type.Name);

                root.Add(itemElement);
                foreach (var property in properties)
                {
                    itemElement.Add(
                        new XElement(
                            property.Name
                            , property.GetValue(item, null))
                            );
                }
            }

            return root;
        }

        /// <summary>
        /// Compress any string source
        /// </summary>
        /// <param name="source">Source content</param>
        /// <returns>If succedded it returns compressed byte array of source string</returns>
        public static byte[] Compress(this string source)
        {
            byte[] sourceArray = Encoding.ASCII.GetBytes(source);
            byte[] result = null;
            using (var ms = new MemoryStream())
            {
                using (var ds = new DeflateStream(ms, CompressionMode.Compress))
                {
                    ds.Write(sourceArray, 0, sourceArray.Length);                    
                }
                result = ms.ToArray();
            }
            return result;
        }

        /// <summary>
        /// Convert any object instance to the string as a JSON format
        /// </summary>
        /// <typeparam name="T">source object's type</typeparam>
        /// <param name="source">source object</param>
        /// <returns>it returns string content of object as a JSON format</returns>
        public static string SerializeToJSON<T>(this T source)
        {
            string result = String.Empty;

            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
            using (MemoryStream mStream = new MemoryStream())
            {
                serializer.WriteObject(mStream, source);
                mStream.Position = 0;
                using (StreamReader reader = new StreamReader(mStream))
                {
                    result = reader.ReadToEnd();
                }
            }

            return result;
        }

        /// <summary>
        /// Read a JSON string and convert it to a object instance
        /// </summary>
        /// <typeparam name="T">type of object</typeparam>
        /// <param name="source">source string</param>
        /// <returns>it returns object instance</returns>
        public static T DeserializeFromJSONString<T>(this string source)
        {
            T result=default(T);
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));

            using (MemoryStream mStream = new MemoryStream())
            {
                using (StreamWriter writer = new StreamWriter(mStream))
                {
                    writer.Write(source);
                    writer.Flush();
                    mStream.Position = 0;
                    result = (T)serializer.ReadObject(mStream);
                }
            }

            return result;
        }

        /// <summary>
        /// Compress a byte array
        /// </summary>
        /// <param name="source">source byte array something like that image array</param>
        /// <param name="compressionType">Compression type. There is a two options; GZip or Deflate</param>
        /// <returns>compressed byte array</returns>
        public static byte[] Compress(this byte[] source, CompressionType compressionType)
        {
            byte[] result = null;

            using (MemoryStream mStream = new MemoryStream())
            {
                switch (compressionType)
                {
                    case CompressionType.GZip:
                        using (GZipStream gzipStream = new GZipStream(mStream, CompressionMode.Compress))
                        {
                            gzipStream.Write(source, 0, source.Length);
                        }
                        break;
                    case CompressionType.Deflate:
                        using (DeflateStream deflateStream = new DeflateStream(mStream, CompressionMode.Compress))
                        {
                            deflateStream.Write(source, 0, source.Length);
                        }
                        break;
                }
                result = mStream.ToArray();
            }
            
            return result;
        }
    }
}
